package com.tao.filter;

import com.alibaba.fastjson.JSON;
import com.tao.pojo.vo.LoginUser;
import com.tao.utils.JwtUtil;
import io.jsonwebtoken.Claims;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;


@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {

    @Autowired
    private RedisTemplate redisTemplate;

    private HashMap<String, Object> map;

    /**
     * 将用户信息存放Security Context Holder
     */
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String path = request.getRequestURI();
        System.out.println(path);
        //获取token
        String token = request.getHeader("token");
        if (!StringUtils.hasText(token)) {
            //放行
            filterChain.doFilter(request, response);
            return;
        }
        //解析token
        String userid;
        try {
            Claims claims = JwtUtil.parseJWT(token);
            userid = claims.getSubject();
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("token非法");
        }
        //从redis中获取用户信息
            LoginUser loginUser = (LoginUser) redisTemplate.opsForValue().get("login:" + userid);

            if (ObjectUtils.isEmpty(loginUser)) {
                response.setContentType("application/json;charset=utf-8");
                map.put("code", 403);
                map.put("msg", "用户未登录");
                response.getWriter().println(JSON.toJSONString(map));
                throw new RuntimeException("用户未登录");
            }
            /**
             authed 类有三个构造函数，其中最常用的构造函数是接受三个参数的构造函数，这三个参数分别代表以下意思：
             principal：表示认证主体，即认证的主体身份信息，通常是用户名或用户对象等。这个参数的类型是 Object，因为它可以是任何类型的认证主体，通常是 String 或者自定义的用户对象。
             credentials：表示认证凭证，即主体提供的凭证，通常是密码或令牌等。这个参数的类型也是 Object，因为它可以是任何类型的认证凭证，通常是 String 或者自定义的凭证对象。
             authorities：表示主体的授权信息，即主体在系统中具有的角色或权限等。这个参数的类型是 Collection<? extends GrantedAuthority>，其中 GrantedAuthority 是一个表示授权信息的接口，通常是 SimpleGrantedAuthority 类型的集合。
             */
            //将用户信息存到security context holder
            UsernamePasswordAuthenticationToken authentic =
                    new UsernamePasswordAuthenticationToken(loginUser.getUser(), null, loginUser.getAuthorities());
            SecurityContextHolder.getContext().setAuthentication(authentic);
            //放行
            filterChain.doFilter(request, response);
        }
}

/**
 * SecurityContextHolder 是 Spring Security 中用于存储和访问当前已认证用户安全上下文的类。安全上下文是一个包含当前认证用户的身份验证信息和授权信息的对象。
 * 在 Spring Security 中，SecurityContextHolder 是通过一个静态方法 getContext() 来访问的。SecurityContextHolder.getContext()
 * 方法返回一个 SecurityContext 对象，这个对象包含了当前已认证用户的身份验证信息和授权信息。
 * SecurityContextHolder 的主要作用是提供一个方便的方式来访问和操作安全上下文。
 * 通过 SecurityContextHolder.getContext() 方法，我们可以获取当前已认证用户的身份验证信息和授权信息，并在程序中进行相应的操作。
 * 例如，我们可以通过 SecurityContextHolder.getContext().getAuthentication() 方法来获取当前已认证用户的 Authentication 对象，然后从这个对象中获取用户名、角色等信息，或者对当前用户进行授权检查等操作。
 * 在 Web 应用程序中，SecurityContextHolder 还提供了一个与请求生命周期相关的存储策略，可以通过 MODE_THREADLOCAL 模式将安全上下文与当前线程绑定。
 * 这意味着在同一线程内的多个方法调用中，可以共享同一个安全上下文，这在某些情况下非常方便。在其他环境中，例如使用异步任务或消息驱动的应用程序，可能需要使用其他存储策略，例如 MODE_GLOBAL 或 MODE_INHERITABLETHREADLOCAL。
 */